//-------------------------------------------------------------------
//	Copyright (c) 2013-2014  Zhirnov Andrey
//	This file is part of the "UX OpwnGL Framework" project.
//	See copyright notice in "UX_GL_Framework.h".
//-------------------------------------------------------------------

#pragma once

#include "VoxelsBlock.h"

namespace Voxels
{

	//
	// Voxels Block Cache In Border
	//

	class VoxelsBlockCacheInBorder
	{
	// types
	public:
		typedef VoxelsBlock::BlockInfo				BlockInfo;
		typedef VoxelsBlock::VoxelsUniData_t		VoxelsUniData_t;
		typedef VoxelsBlock::VoxelsUniConstData_t	VoxelsUniConstData_t;

	private:
		enum {
			BORDER = Helpers::BORDER
		};

		typedef binary_buffer_t						VoxelBuffer_t;


	// variables
	private:
		VoxelBuffer_t			_positiveZVoxel,
								_negativeZVoxel,
								_xyVoxel;
		Ptr< const BlockInfo >	_info;


	// methods
	public:
		VoxelsBlockCacheInBorder () : _info(null) {}

		explicit
		VoxelsBlockCacheInBorder (const BlockInfo &info) : _info(&info) {}

		bool Create (VoxelsUniConstData_t positiveZVoxel, VoxelsUniConstData_t negativeZVoxel, VoxelsUniConstData_t xyVoxel);

		template <typename TVoxel>
		bool Create (const TVoxel &positiveZVoxel, const TVoxel &negativeZVoxel, const TVoxel &xyVoxel);

		bool IsValid () const;

		bool CopyTo (const ivec3 &dstDir, INOUT VoxelsBlock &block) const;
		

	private:
		ivec3	_BlockSize ()					const	{ return _info->blockSize.To<ivec3>(); }
		uint	_BytePerVoxel ()				const	{ return _info->bytePerVoxel; }
		
		VoxelsUniConstData_t _GetVoxel (const ivec3 &dir) const;

		static ivec3 _DstDirToSrc (const ivec3 &dir)	{ return -dir; }
	};

	
/*
=================================================
	Create
=================================================
*/
	inline bool VoxelsBlockCacheInBorder::Create (VoxelsUniConstData_t positiveZVoxel,
					VoxelsUniConstData_t negativeZVoxel, VoxelsUniConstData_t xyVoxel)
	{
		CHECK_ARGUMENT( positiveZVoxel.Size()	== _BytePerVoxel() and
						negativeZVoxel.Size()	== _BytePerVoxel() and
						xyVoxel.Size()			== _BytePerVoxel() );

		_positiveZVoxel.Reallocate( _BytePerVoxel() );
		_negativeZVoxel.Reallocate( _BytePerVoxel() );
		_xyVoxel.Reallocate( _BytePerVoxel() );

		_positiveZVoxel.Copy( positiveZVoxel );
		_negativeZVoxel.Copy( negativeZVoxel );
		_xyVoxel.Copy( xyVoxel );

		return true;
	}
	
/*
=================================================
	Create
=================================================
*/
	template <typename TVoxel>
	inline bool VoxelsBlockCacheInBorder::Create (const TVoxel &positiveZVoxel, const TVoxel &negativeZVoxel, const TVoxel &xyVoxel)
	{
		return Create(	VoxelsUniConstData_t( (const uint8 *) &positiveZVoxel, sizeof(positiveZVoxel) ),
						VoxelsUniConstData_t( (const uint8 *) &negativeZVoxel, sizeof(negativeZVoxel) ),
						VoxelsUniConstData_t( (const uint8 *) &xyVoxel, sizeof(xyVoxel) ) );
	}

/*
=================================================
	IsValid
=================================================
*/
	inline bool VoxelsBlockCacheInBorder::IsValid () const
	{
		return	not _positiveZVoxel.Empty() and
				not _negativeZVoxel.Empty() and
				not _xyVoxel.Empty();
	}
	
/*
=================================================
	CopyTo
=================================================
*/
	inline bool VoxelsBlockCacheInBorder::CopyTo (const ivec3 &dstDir, INOUT VoxelsBlock &block) const
	{
		CHECK_ARGUMENT( IsNotZero( block.Border() ) );

		const ivec3	src_dir	= _DstDirToSrc( dstDir );
		
		const ivec3	block_size	= _BlockSize() - 1;
		const ivec3	dst_start	= Clamp( dstDir * (block_size+1), ivec3(-1), block_size+1 );
		const ivec3	face_size	= (1 - dstDir * dstDir) * block_size;
		
		VoxelsUniConstData_t voxel = _GetVoxel( src_dir );

		for (int z = 0; z <= face_size.z; ++z)
		{
			for (int y = 0; y <= face_size.y; ++y)
			{
				for (int x = 0; x <= face_size.x; ++x)
				{
					const ivec3	c = ivec3( x, y, z );
					const ivec3	d = dst_start + c;

					block.SetVoxel( d, voxel );
				}
			}
		}
		return true;
	}
	
/*
=================================================
	_GetVoxel
=================================================
*/
	inline VoxelsBlockCacheInBorder::VoxelsUniConstData_t
		VoxelsBlockCacheInBorder::_GetVoxel (const ivec3 &dir) const
	{
		if ( dir.z > 0 )
			return _positiveZVoxel;
		else
		if ( dir.z < 0 )
			return _negativeZVoxel;
		else
			return _xyVoxel;
	}


}	// Voxels
